package com.kubepartners.impactmodel.translator.graphml;

import java.io.IOException;
import java.io.OutputStream;
import java.util.Iterator;

import org.jdom.Document;
import org.jdom.Element;
import org.jdom.Namespace;
import org.jdom.output.XMLOutputter;

import com.kubepartners.impactmodel.translator.GraphWriter;
import com.kubepartners.impactmodel.model.Edge;
import com.kubepartners.impactmodel.model.Graph;
import com.kubepartners.impactmodel.model.Node;

public class GraphmlDomWriter implements GraphWriter {
	// Node colours
	private static final String RED = "#FF0000";
	private static final String GREEN = "#00FF00";
	private static final String YELLOW = "#FFFF00";
	private static final String BLACK = "#000000";
	
	// Set up namespaces
	private static final Namespace gml = Namespace.getNamespace("http://graphml.graphdrawing.org/xmlns/graphml");
	private static final Namespace xsi = Namespace.getNamespace("xsi", "http://www.w3.org/2001/XMLSchema-instance");
	private static final Namespace yfiles = Namespace.getNamespace("y", "http://www.yworks.com/xml/graphml");

	public void write(Graph g, OutputStream os) {
		// Set up document root
		Element root = new Element("graphml", gml);
		Document doc = new Document(root);
		doc.setRootElement(root);
		root.addNamespaceDeclaration(gml);
		root.addNamespaceDeclaration(xsi);
		root.addNamespaceDeclaration(yfiles);
		
		// Add Keys
		root.addContent(new Element("key", gml).setAttribute("for", "node").setAttribute("id", "d0").setAttribute("yfiles.type", "nodegraphics"));
		root.addContent(new Element("key", gml).setAttribute("for", "node").setAttribute("id", "d1").setAttribute("attr.name", "description").setAttribute("attr.type", "string"));
		root.addContent(new Element("key", gml).setAttribute("for", "edge").setAttribute("id", "d2").setAttribute("yfiles.type", "edgegraphics"));
		root.addContent(new Element("key", gml).setAttribute("for", "edge").setAttribute("id", "d3").setAttribute("attr.name", "description").setAttribute("attr.type", "string"));
		root.addContent(new Element("key", gml).setAttribute("for", "node").setAttribute("id", "d4").setAttribute("attr.name", "capacity").setAttribute("attr.type", "string"));
		
		// Add graph node
		Element graph = new Element("graph", gml);
		graph.setAttribute("edgedefault", "directed");
		graph.setAttribute("id", "ImpactModel");
		graph.setAttribute("parse.edges", Integer.toString(g.edgeSet().size()));
		graph.setAttribute("parse.nodes", Integer.toString(g.vertexSet().size()));
		root.addContent(graph);
		
		// Add Nodes
		Iterator<Node> nit = g.vertexSet().iterator();
		while (nit.hasNext()) {
			Node n = (Node) nit.next();
			Element ne = new Element("node", gml).setAttribute("id", n.getId());
			Element de = new Element("data", gml).setAttribute("key", "d1");
			Element capacity = new Element("data", gml).setAttribute("key", "d4").setText(String.valueOf(n.getVertexCapacity()));
			de.setText(getNodeData(n));
			ne.addContent(getYfilesNodeData(n));
			ne.addContent(de);
			ne.addContent(capacity);
			graph.addContent(ne);
		}
		
		// Add Edges
		Iterator<Edge> eit = g.edgeSet().iterator();
		while (eit.hasNext()) {
			Edge e = (Edge) eit.next();
			Element ee = new Element("edge", gml).setAttribute("id", e.getId()).setAttribute("source", e.getStartNode().getId()).setAttribute("target", e.getEndNode().getId());
			Element de = new Element("data", gml).setAttribute("key", "d3");
			de.setText(getEdgeData(e));
			ee.addContent(getYfilesEdgeData(e));
			ee.addContent(de);
			graph.addContent(ee);
		}
		
		// Write to OutputStream
		XMLOutputter outputter = new XMLOutputter();
		try {
			outputter.output(doc, os);
		} catch (IOException e) {
			throw new RuntimeException("Cannot write to OutputStream");
		}
	}
	
	private String getEdgeData(Edge e) {
		StringBuffer sb = new StringBuffer();
		sb.append("w=").append(e.getWeight());
		sb.append(",t=").append(e.getThreshold());
		return sb.toString();
	}

	private String getNodeData(Node n) {
		StringBuffer sb = new StringBuffer();
		sb.append("vc=").append(n.getVertexCapacity());
		sb.append(",\r\nic=").append(n.getInducedCapacity());
		sb.append(",\r\ndc=").append(n.getDeliveredCapacity());
		sb.append(",\r\nid=").append(n.getExternalID());
		sb.append(",\r\nlabel=").append(n.getLabel());
		sb.append(",\r\ndescription=").append(n.getDescription());
		sb.append(",\r\nmeasurable=").append(n.isMeasurable());
		return sb.toString();
	}
	
	private Element getYfilesNodeData(Node node) {
		// Set up colors for Node
		String nodeFill = GREEN;
		String nodeBorder = GREEN;
		
		// Delivered capacity is represented by fill colour
		if (node.getDeliveredCapacity() <= 0) {
		    nodeFill = RED;
		} else if (node.getDeliveredCapacity() < 1) {
			 nodeFill = YELLOW;
		}
		
		// Vertex capacity is represented by border colour
		if (node.getVertexCapacity() <= 0) {
		    nodeBorder = RED;
		} else if (node.getVertexCapacity() < 1) {
			nodeBorder = YELLOW;
		}
		
		// Build DOM Element from Node
		Element de = new Element("data", gml).setAttribute("key", "d0");
		Element shape = new Element("ShapeNode", yfiles);
		shape.addContent(new Element("Geometry", yfiles)
			.setAttribute("height", "60.0")
			.setAttribute("width", "60.0")
			.setAttribute("x", node.getGeometry().getX())
			.setAttribute("y", node.getGeometry().getY()));
		shape.addContent(new Element("Fill", yfiles)
			.setAttribute("color", nodeFill)
			.setAttribute("transparent", "false"));
		shape.addContent(new Element("BorderStyle", yfiles)
			.setAttribute("color", nodeBorder)
			.setAttribute("type", "line")
			.setAttribute("width", "2.0"));
		shape.addContent(new Element("NodeLabel", yfiles)
			.setAttribute("alignment", "center")
			.setAttribute("autoSizePolicy", "content")
			.setAttribute("fontFamily", "Dialog")
			.setAttribute("fontSize", "10")
			.setAttribute("fontStyle", "plain")
			.setAttribute("hasBackgroundColor", "false")
			.setAttribute("hasLineColor", "false")
			.setAttribute("height", "18")
			.setAttribute("modelName", "internal")
			.setAttribute("modelPosition", "c")
			.setAttribute("textColor", BLACK)
			.setAttribute("visible", "true")
			.setAttribute("width", "12")
			.setAttribute("x", "9")
			.setAttribute("y", "6")
			.setText(node.getLabel() + "\r\nDC=" + String.valueOf(node.getDeliveredCapacity()) + "\r\nVC=" + String.valueOf(node.getVertexCapacity())));
		shape.addContent(new Element("Shape", yfiles)
			.setAttribute("type", "ellipse"));
		de.addContent(shape);
		return de;
	}
	
	private Element getYfilesEdgeData(Edge edge) {
		Element de = new Element("data", gml).setAttribute("key", "d2");
		Element polyedge = new Element("PolyLineEdge", yfiles);
		polyedge.addContent(new Element("Path", yfiles)
			.setAttribute("sx", "0.0")
			.setAttribute("sy", "0.0")
			.setAttribute("tx", "0.0")
			.setAttribute("ty", "0.0"));
		polyedge.addContent(new Element("LineStyle", yfiles)
			.setAttribute("color", BLACK)
			.setAttribute("type", "line")
			.setAttribute("width", "1.0"));
		polyedge.addContent(new Element("Arrows", yfiles)
			.setAttribute("source", "none")
			.setAttribute("target", "standard"));
		polyedge.addContent(new Element("EdgeLabel", yfiles)
			.setAttribute("alignment", "center")
			.setAttribute("distance", "2.0")
			.setAttribute("fontFamily", "Dialog")
			.setAttribute("fontStyle", "plain")
			.setAttribute("fontSize", "8")
			.setAttribute("hasBackgroundColor", "false")
			.setAttribute("hasLineColor", "false")
			.setAttribute("height", "4.0")
			.setAttribute("modelName", "six_pos")
			.setAttribute("modelPosition", "tail")
			.setAttribute("preferredPlacement", "anywhere")
			.setAttribute("ratio", "0.5")
			.setAttribute("textColor", BLACK)
			.setAttribute("visible", "true")
			.setAttribute("width", "4.0")
			.setAttribute("x", "32")
			.setAttribute("y", "-52")
			.setText("w=" + String.valueOf(edge.getWeight()) + "\r\nt=" + String.valueOf(edge.getThreshold())));
		polyedge.addContent(new Element("BendStyle", yfiles)
			.setAttribute("smoothed", "false"));
		de.addContent(polyedge);
		return de;
	}


}
